Appearance
试写h5手势 1
本节目标:理解“试写h5手势 1”的核心思路,并能把它用于实际开发或面试表达。
import React, { Component } from 'react';
import styles from './index.less';
//两点的距离
function getDistance(p1, p2) {
let x = p2.pageX - p1.pageX,
y = p2.pageY - p1.pageY;
return Math.sqrt(x * x + y * y);
}
//两点的夹角
function getAngle(p1, p2) {
let x = p1.pageX - p2.pageX,
y = p1.pageY - p2.pageY;
return (Math.atan2(y, x) * 180) / Math.PI;
}
//获取中点
function getMidPoint(p1, p2) {
let x = (p1.pageX + p2.pageX) / 2,
y = (p1.pageY + p2.pageY) / 2;
return [x, y];
}
export default class index extends Component {
state = {
startX: 0,
startY: 0,
left: 0,
scaleStyle: [1, 0, 0, 1, 0, 0],//缩放样式
originPoint: [0, 0],//缩放原点
imgNum: 0,//图片数量
currImgIndex: 0,
currImgWidth: 0,
visible: false,
}
componentDidMount() {
const { imgs } = this.props;
// console.log('imgs', imgs);
if (imgs.length > 0) {
this.setState({ imgNum: imgs.length });
}
}
componentDidUpdate() {
const { imgs } = this.props;
const { currImgIndex, currImgWidth: oldCurrImgWidth } = this.state;
if (imgs.length > 0 && this.swiperNode) {
this.createEvent();
const currImgWidth = this.swiperNode.children[`${currImgIndex}`].clientWidth;
// console.log('ref', currImgWidth);
if (oldCurrImgWidth !== currImgWidth) {
this.setState({ currImgWidth })
}
}
}
createEvent = () => {
let isTouch = false; //单指
let isDoubleTouch = false; //双指或多指
let isMove = false;
let start = [];//手势触点集合
let timer = null;
let now, delta;
let startPosition, movePosition, endPosition;
const _this = this;
let left = 0;
//监听基本事件
this.swiperNode.addEventListener('touchstart', handleTouchStart, false);
this.swiperNode.addEventListener('touchmove', handleTouchMove, false);
this.swiperNode.addEventListener('touchend', handleTouchEnd, false);
function handleTouchStart(e) {
// e.preventDefault();
if (e.touches.length >= 2) {
//手势操作,记录触点中心
isDoubleTouch = true;
start = e.touches;
let screenMidPoint = getMidPoint(start[0], start[1]);
let midPoint = [
screenMidPoint[0] - e.target.offsetLeft,
screenMidPoint[1] - e.target.offsetTop
];
_this.setState({ originPoint: midPoint });
} else {
//点击或滑动操作,记录开始坐标和开始时间
isTouch = true;
startPosition = [e.touches[0].pageX, e.touches[0].pageY];
now = Date.now();
}
}
function handleTouchMove(e) {
// clearTimeout(timer);
// e.preventDefault();
let currImgIndex = parseInt(e.target.id) || 0;
const { imgNum, left: rawLeft, currImgWidth } = _this.state;
if (e.touches.length >= 2 && isDoubleTouch) {
// timer = setTimeout(() => {
let scale = parseFloat(getDistance(e.touches[0], e.touches[1]) / getDistance(start[0], start[1]));
let tMatrix = [1, 0, 0, 1, 0, 0];
tMatrix[0] = tMatrix[0] * scale;
tMatrix[3] = tMatrix[3] * scale;
console.log('tMatrix:', tMatrix);
_this.setState({ scaleStyle: tMatrix.join(',') });
// }, 17);
} else if (isTouch) {
//滑动,记录最终位置并计算时间间隔
timer = setTimeout(() => {
delta = Date.now() - now;
movePosition = [e.touches[0].pageX, e.touches[0].pageY];
const hd = Math.abs(startPosition[0] - movePosition[0]);
const vd = Math.abs(startPosition[1] - movePosition[1]);
//快速滑动并且滑动达到一定距离才有效
if ((delta > 0 && delta < 350) && (hd > 10 || vd > 10)) {
clearTimeout(timer);
console.log('object');
//判断滑动方向
const swiperDirection = hd > vd
? (startPosition[0] - movePosition[0] > 0 ? 'left' : 'right')
: (startPosition[1] - movePosition[1] > 0 ? 'top' : 'bottom')
//保存最终位置
endPosition = movePosition;
if (swiperDirection === 'left') {
//最后一张左拉的情况
if (currImgIndex * 1 === imgNum - 1) {
left = rawLeft;
return;
}
left = rawLeft - currImgWidth;
currImgIndex++;
}
if (swiperDirection === 'right') {
//第一张右拉的情况
if (rawLeft === 0) {
left = 0;
return;
}
left = rawLeft + currImgWidth;
currImgIndex--;
}
_this.setState({ left, currImgIndex });
}
}, 100);
};
}
// this.swiperNode.addEventListener('touchend', handleTouchEnd, false);
// this.swiperNode.addEventListener('touchcancel', handleTouchCancel, false);
function handleTouchEnd(e) {
if (isDoubleTouch) {
isDoubleTouch = false;
}
}
function handleTouchCancel(e) { }
}
handleVisible = () => {
this.setState(state => ({
visible: !state.visible
}))
}
render() {
const { imgs, width = '100%', height } = this.props;
const { left, originPoint, scaleStyle, currImgIndex, visible } = this.state;
return (
<div className={styles.imgsViewer} onClick={this.handleVisible}>
{!visible && imgs.length > 0 && <div className={styles.coverWrap} >
<img
style={{ width, height }}
src={`${imgs[0]}?x-image-process=image/resize,m_fill,h_550`}
/>
<div className={styles.count}>{`1/${imgs.length}`}</div>
</div>}
{/*有图片数据才显示
*/}
{visible && imgs.length > 0 &&
<div className={styles.swiperWrap}>
<div
style={{
position: 'absolute',
top: '50%',
left: `${left}px`,
transform: 'translateY(-50%)',
display: 'flex',
transition: 'all 0.5s ease-out 0s',
}}
ref={node => this.swiperNode = node}
>
{imgs.map((imgUrl, index) => {
return (
<div
key={index}
className={styles.imgWrap}
>
<img
id={index}
src={imgUrl}
style={{
transformOrigin: `${originPoint[0]}px ${originPoint[1]}px`,
transform: `matrix(${scaleStyle})`,
}}
/>
</div>
)
})}
</div>
{/*指示器
*/}
<div
style={{
position: 'fixed',
bottom: '20px',
left: '50vw',
transform: 'translate(-50%)',
padding: 5,
zIndex: 99,
}}
>
{imgs.map((_, index) => (
<div
key={index}
style={{
display: 'inline-block',
width: 10,
height: 10,
background: '#ddd',
borderRadius: '50%',
marginLeft: 10,
boxShadow: currImgIndex === index ? '0 0 4px 2px #fff' : ''
}}></div>
))}
</div>
</div>}
</div >
)
}
}